package com.baidu.iit.pxp.behavior;

import com.baidu.iit.pvm.ActivityIllegalArgumentException;
import com.baidu.iit.pvm.delegate.ActivityExecution;
import com.baidu.iit.pvm.process.ActivityImpl;
import com.baidu.iit.pxp.entity.ExecutionEntity;

import java.util.ArrayList;
import java.util.List;

/**
 * User: huangweili
 * Date: 14-4-27
 * Time: 上午9:20
 */
public class ParallelMultiInstanceBehavior extends MultiInstanceActivityBehavior {

    public ParallelMultiInstanceBehavior(ActivityImpl activity, AbstractBpmnActivityBehavior originalActivityBehavior) {
        super(activity, originalActivityBehavior);
    }

    protected void createInstances(ActivityExecution execution) throws Exception {
        int nrOfInstances = resolveNrOfInstances(execution);
        if (nrOfInstances <= 0) {
            throw new ActivityIllegalArgumentException("Invalid number of instances: must be positive integer value"
                    + ", but was " + nrOfInstances);
        }

        setLoopVariable(execution, NUMBER_OF_INSTANCES, nrOfInstances);
        setLoopVariable(execution, NUMBER_OF_COMPLETED_INSTANCES, 0);
        setLoopVariable(execution, NUMBER_OF_ACTIVE_INSTANCES, nrOfInstances);

        List<ActivityExecution> concurrentExecutions = new ArrayList<ActivityExecution>();
        for (int loopCounter = 0; loopCounter < nrOfInstances; loopCounter++) {
            ActivityExecution concurrentExecution = execution.createExecution();
            concurrentExecution.setActive(true);
            concurrentExecution.setConcurrent(true);
            concurrentExecution.setScope(false);
            if (isExtraScopeNeeded()) {
                ActivityExecution extraScopedExecution = concurrentExecution.createExecution();
                extraScopedExecution.setActive(true);
                extraScopedExecution.setConcurrent(false);
                extraScopedExecution.setScope(true);
                concurrentExecution = extraScopedExecution;
            }

            concurrentExecutions.add(concurrentExecution);
            logLoopDetails(concurrentExecution, "initialized", loopCounter, 0, nrOfInstances, nrOfInstances);
        }

        for (int loopCounter = 0; loopCounter < nrOfInstances; loopCounter++) {
            ActivityExecution concurrentExecution = concurrentExecutions.get(loopCounter);
            if (concurrentExecution.isActive() && !concurrentExecution.isEnded()
                    && concurrentExecution.getParent().isActive()
                    && !concurrentExecution.getParent().isEnded()) {
                setLoopVariable(concurrentExecution, getCollectionElementIndexVariable(), loopCounter);
                executeOriginalBehavior(concurrentExecution, loopCounter);
            }
        }

        if (!concurrentExecutions.isEmpty()) {
            ExecutionEntity executionEntity = (ExecutionEntity) execution;
            executionEntity.setActive(false);
        }
    }

    public void leave(ActivityExecution execution) {
        callActivityEndListeners(execution);

        int loopCounter = getLoopVariable(execution, getCollectionElementIndexVariable());
        int nrOfInstances = getLoopVariable(execution, NUMBER_OF_INSTANCES);
        int nrOfCompletedInstances = getLoopVariable(execution, NUMBER_OF_COMPLETED_INSTANCES) + 1;
        int nrOfActiveInstances = getLoopVariable(execution, NUMBER_OF_ACTIVE_INSTANCES) - 1;

        if (isExtraScopeNeeded()) {
            ExecutionEntity extraScope = (ExecutionEntity) execution;
            execution = execution.getParent();
            extraScope.remove();
        }

        setLoopVariable(execution.getParent(), NUMBER_OF_COMPLETED_INSTANCES, nrOfCompletedInstances);
        setLoopVariable(execution.getParent(), NUMBER_OF_ACTIVE_INSTANCES, nrOfActiveInstances);
        logLoopDetails(execution, "instance completed", loopCounter, nrOfCompletedInstances, nrOfActiveInstances, nrOfInstances);

        ExecutionEntity executionEntity = (ExecutionEntity) execution;
        executionEntity.inactivate();
        executionEntity.getParent().forceUpdate();

        List<ActivityExecution> joinedExecutions = executionEntity.findInactiveConcurrentExecutions(execution.getActivity());
        if (joinedExecutions.size() == nrOfInstances || completionConditionSatisfied(execution)) {
            List<ExecutionEntity> executionsToRemove = new ArrayList<ExecutionEntity>();
            for (ActivityExecution childExecution : executionEntity.getParent().getExecutions()) {
                if (childExecution.isActive()) {
                    executionsToRemove.add((ExecutionEntity) childExecution);
                }
            }
            for (ExecutionEntity executionToRemove : executionsToRemove) {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Execution {} still active, but multi-instance is completed. Removing this execution.", executionToRemove);
                }
                executionToRemove.inactivate();
                executionToRemove.deleteCascade("multi-instance completed");
            }
            executionEntity.takeAll(executionEntity.getActivity().getOutgoingTransitions(), joinedExecutions);
        }
    }

}
